﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace TMScore_Subsets
{
    class DocumentsManager // handle all the documents, create the docs, make tfidf,send the docs for  the subset TMscore calculation
    {

        public List<Document> allDocuments { get; set; } // keeps all the documents in the repository
        public Dictionary<int, int> sortedNumOfAppearenceInRepository;// all hashed ngram and it num of appearence in repository
        public List<int> topNgrams; // keeps the top nGram that we want to save from all the nGrams

        public DocumentsManager()
        {
            allDocuments = new List<Document>();
            sortedNumOfAppearenceInRepository = new Dictionary<int, int>();
            topNgrams = new List<int>();
        }
        /// <summary>
        /// main function- forech document the func build a document object, calculate Fingerprints,tfidf,send to create users from the recievers list
        /// </summary>
        /// <param name="allItem"></param>
        /// <param name="m_AllDocs"></param>
        /// <returns></returns>
        public List<User> CreateAllDocuments(Tuple<int, int, bool, bool, Dictionary<string, Tuple<string, List<string>, DateTime, double>>, int, int> allItem, Dictionary<string, string> m_AllDocs)
        {
            
            UsersManager manager = new UsersManager();
            foreach (string currentDoc in allItem.Item5.Keys) // forech doc
            {
                Document d = new Document(currentDoc, allItem.Item5[currentDoc], m_AllDocs[currentDoc]);//Dictionary<fileName ,tuple <all text, list of recivers,date,tmscore>
                d.MakeNGramsFromText(allItem.Item1, allItem.Item2, allItem.Item3); // make ngrams
                FingerprintsGenerator fg = new FingerprintsGenerator();
                List<string> result = m_AllDocs[currentDoc].Split(new char[] { ' ' }).ToList();
                Dictionary<int, int> toReturn = fg.GenerateFingerprints(result);
                d.Fingerprints = toReturn;
                manager.CreateUser(d);
                allDocuments.Add(d);  

            }
          CreateTfIdf(allItem.Item6);
            return manager.usersList;
        }

        /// <summary>
        /// this function update the global count of n gram in documents
        /// </summary>
        /// <param name="DB"></param>
        /// <param name="top"></param>
        public void CreateTfIdf(int top)
        {

            foreach (Document currentDoc in allDocuments)
            {
                foreach (int currentNgram in currentDoc.m_allHashedNGrams.Keys)
                {
                    if (!sortedNumOfAppearenceInRepository.ContainsKey(currentNgram)) // not exist in dic
                        sortedNumOfAppearenceInRepository.Add(currentNgram, 1);

                    else  // exist, update num of appearence
                        sortedNumOfAppearenceInRepository[currentNgram]++;
                }
            }
            SelectTopNGrmas(top); // select to k best features
            calcultetfidf(); // calculate the weights only for the k features
        }

        // <summary>
        // select a number of ngram which will appear in topsNgram 
        // </summary>
        // <param name="DB"></param>
        ///// <param name="numOfNGrams"></param>
        private void SelectTopNGrmas(int numOfNGrams)
        {
            if (numOfNGrams > sortedNumOfAppearenceInRepository.Count)
            {
                Console.WriteLine("the max size you can insert is must be smaller the total nGrams!!");
                Console.WriteLine("please insert a new number of nGrams for the feature selection which small than: " + sortedNumOfAppearenceInRepository.Count);
                numOfNGrams = int.Parse(Console.ReadLine());
            }
            sortedNumOfAppearenceInRepository = sortedNumOfAppearenceInRepository.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
            int i = 0;
            foreach (KeyValuePair<int, int> item in sortedNumOfAppearenceInRepository)
            {
                if (i == numOfNGrams)
                    return;
                else
                {
                    topNgrams.Add(item.Key);
                    i++;
                }
            }
        }
        //<summary>
        //calculte the tf idf for each document and foreach ngram in topngram
        //</summary>
        //<param name="DB"></param>
        public void calcultetfidf()
        {
            TextWriter tw = new StreamWriter("tf_idf.txt");
            tw.Close();
            List<double> tempList = new List<double>();  // list who keeps all n gram
            foreach (Document currentDoc in allDocuments)
            {

                foreach (int currentNgram in topNgrams)  // foreach document foreach ngram making a tfidf calculating
                {
                    if (!currentDoc.m_allHashedNGrams.ContainsKey(currentNgram))// if ngram not exist in the current document
                        tempList.Add(0);
                    else
                    {
                        double idf = Math.Log((double)allDocuments.Count / (double)sortedNumOfAppearenceInRepository[currentNgram], 2);
                        double tf = (double)currentDoc.m_allHashedNGrams[currentNgram] / (double)currentDoc.m_mostCommonNGram;
                        tempList.Add(tf * idf);
                    }



                }//foreach ngram
               // currentDoc.tfIdfList = new List<double>(tempList);
                writeIntoFile(currentDoc.m_Name,tempList);
                tempList.Clear();
            }//foreach doc
        } //calculatetfidf

        /// <summary>
        /// write the tf-idf to a file
        /// </summary>
        /// <param name="currentDoc"></param>
        /// <param name="tempList"></param>
         public void writeIntoFile( string currentDoc,List<double>tempList)
         {
             int i = 0;
             System.IO.StreamWriter file ;
            // create a writer and open the file
             
             using (file= new System.IO.StreamWriter(@"tf_idf.txt", true))
             {
                 
                 file.WriteLine(currentDoc + ":");
                 foreach (double d in tempList)
                 // write a line of text to the file
                 {
                     if (i < tempList.Count - 1)
                     {
                         file.Write(d + ",");
                         i++;
                     }
                     else
                     {
                        
                         file.WriteLine(" ");
                     }
                 }
             } 
           
            // close the stream
            file.Close();
         }
    }
}
